/*
 * @file chip_syscon.h
 * @author suyong_yq@126.com
 *
 * @brief LPC8xx SYSCON driver
 * @note
 * 基于LPCOpen中chip_syscon_8xx及chip_clock_8xx驱动程序改写
 */

#ifndef __SYSCON_8XX_H_
#define __SYSCON_8XX_H_

#include "chip.h"

#ifdef __cplusplus
extern "C" {
#endif

/** @defgroup SYSCON_8XX CHIP: LPC8xx System and Control Driver
 * @ingroup CHIP_8XX_Drivers
 * @{
 */

/**
 * System reset status values
 */
#define SYSCON_RST_POR    (1 << 0)  /*!< POR reset status */
#define SYSCON_RST_EXTRST (1 << 1)  /*!< External reset status */
#define SYSCON_RST_WDT    (1 << 2)  /*!< Watchdog reset status */
#define SYSCON_RST_BOD    (1 << 3)  /*!< Brown-out detect reset status */
#define SYSCON_RST_SYSRST (1 << 4)  /*!< software system reset status */

/**
 * Peripheral interrupt wakeup events values
 */
#define SYSCON_WAKEUP_SPI0TINT    (1 << 0)  /*!< SPI0 interrupt wake-up */
#define SYSCON_WAKEUP_SPI1INT     (1 << 1)  /*!< SPI0 interrupt wake-up */
#define SYSCON_WAKEUP_USART0INT   (1 << 3)  /*!< USART0 interrupt wake-up */
#define SYSCON_WAKEUP_USART1INT   (1 << 4)  /*!< USART1 interrupt wake-up */
#define SYSCON_WAKEUP_USART2INT   (1 << 5)  /*!< USART2 interrupt wake-up */
#define SYSCON_WAKEUP_I2C0INT     (1 << 8)  /*!< I2C0 interrupt wake-up */
#define SYSCON_WAKEUP_I2C1INT     (1 << 7)  /*!< I2C1 interrupt wake-up [Available only on LPC82X] */
#define SYSCON_WAKEUP_I2C2INT     (1 << 21) /*!< I2C2 interrupt wake-up [Available only on LPC82X] */
#define SYSCON_WAKEUP_I2C3INT     (1 << 22) /*!< I2C3 interrupt wake-up [Available only on LPC82X] */
#define SYSCON_WAKEUP_WWDTINT     (1 << 12) /*!< WWDT interrupt wake-up */
#define SYSCON_WAKEUP_BODINT      (1 << 13) /*!< Brown Out Detect (BOD) interrupt wake-up */
#define SYSCON_WAKEUP_WKTINT      (1 << 15) /*!< Self wake-up timer interrupt wake-up */
#define SYSCON_WAKEUP_I2CINT      SYSCON_WAKEUP_I2C0INT /*!< Same as #SYSCON_WAKEUP_I2CINT */

/**
 * Deep sleep setup values
 */
#define SYSCON_DEEPSLP_BOD_PD    (1 << 3)   /*!< BOD power-down control in Deep-sleep mode, powered down */
#define SYSCON_DEEPSLP_WDTOSC_PD (1 << 6)   /*!< Watchdog oscillator power control in Deep-sleep, powered down */

/**
 * Deep sleep to wakeup and power state setup values
 */
#define SYSCON_SLPWAKE_IRCOUT_PD (1 << 0)   /*!< IRC oscillator output wake-up configuration */
#define SYSCON_SLPWAKE_IRC_PD    (1 << 1)   /*!< IRC oscillator power-down wake-up configuration */
#define SYSCON_SLPWAKE_FLASH_PD  (1 << 2)   /*!< Flash wake-up configuration */
#define SYSCON_SLPWAKE_BOD_PD    (1 << 3)   /*!< BOD wake-up configuration */
#define SYSCON_SLPWAKE_ADC_PD    (1 << 4)   /*!< ADC wake-up configuration [Available only on LPC82x] */
#define SYSCON_SLPWAKE_SYSOSC_PD (1 << 5)   /*!< System oscillator wake-up configuration */
#define SYSCON_SLPWAKE_WDTOSC_PD (1 << 6)   /*!< Watchdog oscillator wake-up configuration */
#define SYSCON_SLPWAKE_SYSPLL_PD (1 << 7)   /*!< System PLL wake-up configuration */
#define SYSCON_SLPWAKE_ACMP_PD   (1 << 15)  /*!< Analog comparator wake-up configuration */

/*!
 * @brief 选定外设的供电开关
 */
enum SYSCON_PeriphPowerSel_T
{
    eSYSCON_PeriphPowerSel_IRCOUT = (1 << 0),   /*!< IRC oscillator output wake-up configuration */
    eSYSCON_PeriphPowerSel_IRC    = (1 << 1),   /*!< IRC oscillator power-down wake-up configuration */
    eSYSCON_PeriphPowerSel_FLASH  = (1 << 2),   /*!< Flash wake-up configuration */
    eSYSCON_PeriphPowerSel_BOD    = (1 << 3),   /*!< BOD wake-up configuration */
    eSYSCON_PeriphPowerSel_ADC    = (1 << 4),   /*!< ADC wake-up configuration [Available only on LPC82x] */
    eSYSCON_PeriphPowerSel_SYSOSC = (1 << 5),   /*!< System oscillator wake-up configuration */
    eSYSCON_PeriphPowerSel_WDTOSC = (1 << 6),   /*!< Watchdog oscillator wake-up configuration */
    eSYSCON_PeriphPowerSel_SYSPLL = (1 << 7),   /*!< System PLL wake-up configuration */
    eSYSCON_PeriphPowerSel_ACMP   = (1 << 15),  /*!< Analog comparator wake-up configuration */
};


/**
 * Non-Maskable Interrupt Enable/Disable value
 */
#define SYSCON_NMISRC_ENABLE   ((uint32_t) 1 << 31) /*!< Enable the Non-Maskable Interrupt (NMI) source */

/**
 * @brief IOCON Perpipheral Clock divider selction for input filter
 * sampling clock
 */
typedef enum CHIP_PIN_CLKDIV {
    IOCONCLKDIV0 = 0,               /*!< Clock divider 0 */
    IOCONCLKDIV1,                   /*!< Clock divider 1 */
    IOCONCLKDIV2,                   /*!< Clock divider 2 */
    IOCONCLKDIV3,                   /*!< Clock divider 3 */
    IOCONCLKDIV4,                   /*!< Clock divider 4 */
    IOCONCLKDIV5,                   /*!< Clock divider 5 */
    IOCONCLKDIV6,                   /*!< Clock divider 6 */
    IOCONCLK_MAX = IOCONCLKDIV6     /*!< Top value used to reverse the dividers */
} CHIP_PIN_CLKDIV_T;

/* Reserved bits masks for registers */
#define SYSCON_SYSMEMREMAP_RESERVED     (~3)
#define SYSCON_SYSPLLCTRL_RESERVED      (~0x7f)
#define SYSCON_SYSPLLSTAT_RESERVED      (~1)
#define SYSCON_SYSOSCCTRL_RESERVED      (~3)
#define SYSCON_WDTOSCCTRL_RESERVED      (~0x1ff)
#define SYSCON_SYSRSTSTAT_RESERVED      (~0x1f)
#define SYSCON_SYSPLLCLKSEL_RESERVED    (~3)
#define SYSCON_SYSPLLCLKUEN_RESERVED    (~1)
#define SYSCON_MAINCLKSEL_RESERVED      (~3)
#define SYSCON_MAINCLKUEN_RESERVED      (~1)
#define SYSCON_SYSAHBCLKDIV_RESERVED    (~0xff)
#define SYSCON_UARTCLKDIV_RESERVED      (~0xff)
#define SYSCON_CLKOUTSEL_RESERVED       (~3)
#define SYSCON_CLKOUTUEN_RESERVED       (~1)
#define SYSCON_CLKOUTDIV_RESERVED       (~0xff)
#define SYSCON_UARTFRGDIV_RESERVED      (~0xff)
#define SYSCON_UARTFRGMULT_RESERVED     (~0xff)
#define SYSCON_EXTTRACECMD_RESERVED     (~3)
#define SYSCON_IOCONCLKDIV_RESERVED     (~0xff)
#define SYSCON_BODCTRL_RESERVED         (~0x1f)
#define SYSCON_SYSTCKCAL_RESERVED       0xfc000000
#define SYSCON_IRQLATENCY_RESERVED      (~0xff)
#define SYSCON_NMISRC_RESERVED          (~(0x1f|(1u<<31)))
#define SYSCON_PINTSEL_RESERVED         (~0x3f)
#define SYSCON_STARTERP0_RESERVED       (~0xff)
#if defined(CHIP_LPC82X)
#define SYSCON_PRESETCTRL_RESERVED      0xfffe2000
#define SYSCON_SYSAHBCLKCTRL_RESERVED   0xda100000
#define SYSCON_PIOPORCAP0_RESERVED      0xfffc0000
#define SYSCON_STARTERP1_RESERVED       ((1<<2)|(1<<6)|(7<<9)|(1<<14)|0xff9f0000)
#else
#define SYSCON_PRESETCTRL_RESERVED      0xffffe000
#define SYSCON_SYSAHBCLKCTRL_RESERVED   0xfff00000
#define SYSCON_PIOPORCAP0_RESERVED      0xffffc000
#define SYSCON_STARTERP1_RESERVED       ((1<<2)|(3<<6)|(7<<9)|(1<<14)|(0x1f<<16)|0xff800000)
#endif
/* The following have reserved bits, but they are specially handled elsewhere. */
/* #define SYSCON_PDSLEEPCFG_RESERVED      (~(1<<3)|(3<<4)|(1<<6)) */
/* #define SYSCON_PDAWAKECFG_RESERVED */
/* #define SYSCON_PDRUNCFG_RESERVED   */

/**
 * System memory remap modes used to remap interrupt vectors
 */
typedef enum CHIP_SYSCON_BOOT_MODE_REMAP {
    REMAP_BOOT_LOADER_MODE, /*!< Interrupt vectors are re-mapped to Boot ROM */
    REMAP_USER_RAM_MODE,    /*!< Interrupt vectors are re-mapped to user static RAM */
    REMAP_USER_FLASH_MODE   /*!< Interrupt vectors are not re-mapped and reside in Flash */
} CHIP_SYSCON_BOOT_MODE_REMAP_T;

/**
 * Peripheral reset identifiers
 */
enum SYSCON_PeriphResetSel_T
{
    eSYSCON_PeriphReset_SPI0 = (1U << 0),         /*!< SPI0 reset control */
    eSYSCON_PeriphReset_SPI1 = (1U << 1),         /*!< SPI1 reset control */
    eSYSCON_PeriphReset_UARTFBRG = (1U << 2),     /*!< UART fractional baud rate generator reset control */
    eSYSCON_PeriphReset_USART0 = (1U << 3),       /*!< USART0 reset control */
    eSYSCON_PeriphReset_USART1 = (1U << 4),       /*!< USART1 reset control */
    eSYSCON_PeriphReset_USART2 = (1U << 5),       /*!< USART2 reset control */
    eSYSCON_PeriphReset_I2C0 = (1U << 6),         /*!< I2C0 reset control */
    eSYSCON_PeriphReset_MRT = (1U << 7),          /*!< MRT reset control */
    eSYSCON_PeriphReset_SCT = (1U << 8),          /*!< SCT reset control */
    eSYSCON_PeriphReset_WKT = (1U << 9),          /*!< Self wake-up timer (WKT) control */
    eSYSCON_PeriphReset_GPIO = (1U << 10),         /*!< GPIO reset control */
    eSYSCON_PeriphReset_FLASH = (1U << 11),        /*!< FLASH reset control */
    eSYSCON_PeriphReset_ACMP = (1U << 12),         /*!< ACMP reset control */
    eSYSCON_PeriphReset_I2C1 = (1U << 14),    /*!< I2C1 reset control [Available only in LPC82x] */
    eSYSCON_PeriphReset_I2C2 = (1U << 15),         /*!< I2C2 reset control [Available only in LPC82x] */
    eSYSCON_PeriphReset_I2C3 = (1U << 16),         /*!< I2C3 reset control [Available only in LPC82x] */
    eSYSCON_PeriphReset_ADC = (1U << 24),
    eSYSCON_PeriphReset_DMA = (1U << 29),
};

/* Reset Alias */
#define RESET_I2C    RESET_I2C0

/**
 * Brown-out detector reset level
 */
typedef enum CHIP_SYSCON_BODRSTLVL {
    SYSCON_BODRSTLVL_0, /*!< Brown-out reset at 1.46 ~ 1.63v */
    SYSCON_BODRSTLVL_1, /*!< Brown-out reset at 2.06v ~ 2.15v */
    SYSCON_BODRSTLVL_2, /*!< Brown-out reset at 2.35v ~ 2.43v */
    SYSCON_BODRSTLVL_3, /*!< Brown-out reset at 2.63v ~ 2.71v */
} CHIP_SYSCON_BODRSTLVL_T;

/**
 * Brown-out detector interrupt level
 */
typedef enum CHIP_SYSCON_BODRINTVAL {
    SYSCON_BODINTVAL_LVL0,  /* Brown-out interrupt at 1.65 ~ 1.80v */
    SYSCON_BODINTVAL_LVL1,  /* Brown-out interrupt at 2.22v ~ 2.35v*/
    SYSCON_BODINTVAL_LVL2,  /* Brown-out interrupt at 2.52v ~ 2.66v */
    SYSCON_BODINTVAL_LVL3,  /* Brown-out interrupt at 2.80v ~ 2.90v */
} CHIP_SYSCON_BODRINTVAL_T;

/**
 * @brief   Re-map interrupt vectors
 * @param   remap   : system memory map value
 * @return  Nothing
 */
static inline void Chip_SYSCON_Map(CHIP_SYSCON_BOOT_MODE_REMAP_T remap)
{
    LPC_SYSCON->SYSMEMREMAP = (uint32_t) remap;
}

/**
 * @brief   Assert reset for a peripheral
 * @param   periphMsk  : Peripheral to assert reset for. See to #SYSCON_PeriphResetSel_T.
 * @return  Nothing
 * @note    The peripheral will stay in reset until reset is de-asserted. Call
 * Chip_SYSCON_DeassertPeriphReset() to de-assert the reset
 */
static inline void Chip_SYSCON_AssertPeriphReset(uint32_t periphMsk)
{
    LPC_SYSCON->PRESETCTRL &= ~(periphMsk | SYSCON_PRESETCTRL_RESERVED);
}

/**
 * @brief   De-assert reset for a peripheral
 * @param   periphMsk  : Peripheral to de-assert reset for. See to #SYSCON_PeriphResetSel_T.
 * @return  Nothing
 */
static inline void Chip_SYSCON_DeassertPeriphReset(uint32_t periphMsk)
{
    LPC_SYSCON->PRESETCTRL = periphMsk | (LPC_SYSCON->PRESETCTRL & ~SYSCON_PRESETCTRL_RESERVED);
}

/**
 * @brief   Resets a peripheral
 * @param   periphMsk  :   Peripheral to reset. See to #SYSCON_PeriphResetSel_T.
 * @return  Nothing
 */
static inline void Chip_SYSCON_PeriphReset(uint32_t periphMsk)
{
    Chip_SYSCON_AssertPeriphReset(periphMsk);
    Chip_SYSCON_DeassertPeriphReset(periphMsk);
}

/**
 * @brief   Get system reset status
 * @return  An Or'ed value of SYSCON_RST_*
 * @note    This function returns the detected reset source(s).
 */
static inline uint32_t Chip_SYSCON_GetSystemRSTStatus(void)
{
    return LPC_SYSCON->SYSRSTSTAT & ~SYSCON_SYSRSTSTAT_RESERVED;
}

/**
 * @brief   Clear system reset status
 * @param   reset   : An Or'ed value of SYSCON_RST_* status to clear
 * @return  Nothing
 * @note    This function clears the specified reset source(s).
 */
static inline void Chip_SYSCON_ClearSystemRSTStatus(uint32_t reset)
{
    LPC_SYSCON->SYSRSTSTAT = reset;
}

/**
 * @brief   Read POR captured PIO status
 * @return  captured POR PIO status
 * @note    Some devices only support index 0.
 */
static inline uint32_t Chip_SYSCON_GetPORPIOStatus(void)
{
    return LPC_SYSCON->PIOPORCAP0 & ~SYSCON_PIOPORCAP0_RESERVED;
}

/**
 * @brief   Set brown-out detection interrupt and reset levels
 * @param   rstlvl  : Brown-out detector reset level
 * @param   intlvl  : Brown-out interrupt level
 * @return  Nothing
 * @note    Brown-out detection reset will be disabled upon exiting this function.
 * Use Chip_SYSCON_EnableBODReset() to re-enable
 */
static inline void Chip_SYSCON_SetBODLevels(CHIP_SYSCON_BODRSTLVL_T rstlvl,
                                            CHIP_SYSCON_BODRINTVAL_T intlvl)
{
    LPC_SYSCON->BODCTRL = ((uint32_t) rstlvl) | (((uint32_t) intlvl) << 2);
}

/**
 * @brief   Enable brown-out detection reset
 * @return  Nothing
 */
static inline void Chip_SYSCON_EnableBODReset(void)
{
    LPC_SYSCON->BODCTRL = (1 << 4) | (LPC_SYSCON->BODCTRL & ~SYSCON_BODCTRL_RESERVED);
}

/**
 * @brief   Disable brown-out detection reset
 * @return  Nothing
 */
static inline void Chip_SYSCON_DisableBODReset(void)
{
    LPC_SYSCON->BODCTRL &= ~((1 << 4) | SYSCON_BODCTRL_RESERVED);
}

/**
 * @brief   Set System tick timer calibration value
 * @param   sysCalVal   : System tick timer calibration value
 * @return  Nothing
 */
static inline void Chip_SYSCON_SetSYSTCKCAL(uint32_t sysCalVal)
{
    LPC_SYSCON->SYSTCKCAL = sysCalVal;
}

/**
 * @brief   Set System IRQ latency
 * @param   latency : Latency in clock ticks
 * @return  Nothing
 * @note    Sets the IRQ latency, a value between 0 and 255 clocks. Lower
 * values allow better latency
 */
static inline void Chip_SYSCON_SetIRQLatency(uint32_t latency)
{
    LPC_SYSCON->IRQLATENCY = latency;
}

/**
 * @brief   Get System IRQ latency value
 * @return  IRQ Latency in clock ticks
 */
static inline uint32_t Chip_SYSCON_GetIRQLatency(void)
{
    return LPC_SYSCON->IRQLATENCY & ~SYSCON_IRQLATENCY_RESERVED;
}

/**
 * @brief   Set source for non-maskable interrupt (NMI)
 * @param   intsrc  : IRQ number to assign to the NMI
 * @return  Nothing
 * @note    The NMI source will be disabled upon exiting this function. Use the
 * Chip_SYSCON_EnableNMISource() function to enable the NMI source
 */
static inline void Chip_SYSCON_SetNMISource(uint32_t intsrc)
{
    /* Disable NMI first */
    LPC_SYSCON->NMISRC &= ~(SYSCON_NMISRC_ENABLE | SYSCON_NMISRC_RESERVED);

    /* Set new NMI source. */
    LPC_SYSCON->NMISRC = intsrc;
}

/**
 * @brief   Enable interrupt used for NMI source
 * @return  Nothing
 */
static inline void Chip_SYSCON_EnableNMISource(void)
{
    LPC_SYSCON->NMISRC = SYSCON_NMISRC_ENABLE | (LPC_SYSCON->NMISRC & ~SYSCON_NMISRC_RESERVED);
}

/**
 * @brief   Disable interrupt used for NMI source
 * @return  Nothing
 */
static inline void Chip_SYSCON_DisableNMISource(void)
{
    LPC_SYSCON->NMISRC &= ~(SYSCON_NMISRC_ENABLE | SYSCON_NMISRC_RESERVED);
}

/**
 * @brief   Setup a pin source for the pin interrupts (0-7)
 * @param   intno   : IRQ number
 * @param   pin     : pin number (see comments)
 * @return  Nothing
 * @note    For each pin (0-7) that supports an interrupt, the pin number is assigned
 * to that interrupt with this function. Values 0-17 map to pins PIO0-0 to
 * PIO0-17 (For LPC82X Values from 0-28 could be used for PIO0-28).
 */
static inline void Chip_SYSCON_SetPinInterrupt(uint32_t pintIrqIdx, uint32_t pinIdx)
{
    LPC_SYSCON->PINTSEL[pintIrqIdx] = (uint32_t) pinIdx;
}

/**
 * @brief   Enables a pin's (PINT) wakeup logic
 * @param   pin : pin number
 * @return  Nothing
 * @note    Different devices support different pins, see the user manual
 * for supported pins
 */
static inline void Chip_SYSCON_EnablePINTWakeup(uint32_t pin)
{
    LPC_SYSCON->STARTERP0 = (1 << pin) | (LPC_SYSCON->STARTERP0 & ~SYSCON_STARTERP0_RESERVED);
}

/**
 * @brief   Disables a pin's (PINT) wakeup logic
 * @param   pin : pin number
 * @return  Nothing
 * @note    Different devices support different pins, see the user manual for supported pins.
 */
static inline void Chip_SYSCON_DisablePINTWakeup(uint32_t pin)
{
    LPC_SYSCON->STARTERP0 &= ~((1 << pin) | SYSCON_STARTERP0_RESERVED);
}

/**
 * @brief   Enables peripheral's wakeup logic
 * @param   periphmask  : OR'ed values of SYSCON_WAKEUP_* for wakeup
 * @return  Nothing
 */
static inline void Chip_SYSCON_EnablePeriphWakeup(uint32_t periphmask)
{
    LPC_SYSCON->STARTERP1 = periphmask | (LPC_SYSCON->STARTERP0 & ~SYSCON_STARTERP0_RESERVED);
}

/**
 * @brief   Disables peripheral's wakeup logic
 * @param   periphmask  : OR'ed values of SYSCON_WAKEUP_* for wakeup
 * @return  Nothing
 */
static inline void Chip_SYSCON_DisablePeriphWakeup(uint32_t periphmask)
{
    LPC_SYSCON->STARTERP1 &= ~(periphmask | SYSCON_STARTERP1_RESERVED);
}

/**
 * @brief   Returns current deep sleep mask
 * @return  OR'ed values of SYSCON_DEEPSLP_* values
 * @note    A high bit indicates the peripheral will power down on deep sleep.
 */
static inline uint32_t Chip_SYSCON_GetDeepSleepPD(void)
{
    return LPC_SYSCON->PDSLEEPCFG;
}

/**
 * @brief   Return current wakup mask
 * @return  OR'ed values of SYSCON_SLPWAKE_* values
 * @note    A high state indicates the peripehral will powerup on wakeup.
 */
static inline uint32_t Chip_SYSCON_GetWakeup(void)
{
    return LPC_SYSCON->PDAWAKECFG;
}

/**
 * @brief   Power up one or more blocks or peripherals
 * @return  OR'ed values of SYSCON_SLPWAKE_* values
 * @note    A high state indicates the peripheral is powered down.
 */
static inline uint32_t Chip_SYSCON_GetPowerStates(void)
{
    return LPC_SYSCON->PDRUNCFG;
}

/**
 * @brief   Return the device ID
 * @return  Device ID
 */
static inline uint32_t Chip_SYSCON_GetDeviceID(void)
{
    return LPC_SYSCON->DEVICE_ID;
}

/**
 * @brief   Setup deep sleep behaviour for power down
 * @param   sleepmask   : OR'ed values of SYSCON_DEEPSLP_* values (high to powerdown on deepsleep)
 * @return  Nothing
 * @note    This must be setup prior to using deep sleep. See the user manual
 * *(PDSLEEPCFG register) for more info on setting this up. This function selects
 * which peripherals are powered down on deep sleep.
 * This function should only be called once with all options for power-down
 * in that call
 */
void Chip_SYSCON_SetDeepSleepPD(uint32_t sleepmask);

/**
 * @brief   Setup wakeup behaviour from deep sleep
 * @param   wakeupmask  : OR'ed values of SYSCON_SLPWAKE_* values (high is powered down)
 * @return  Nothing
 * @note    This must be setup prior to using deep sleep. See the user manual
 * *(PDWAKECFG register) for more info on setting this up. This function selects
 * which peripherals are powered up on exit from deep sleep.
 * This function should only be called once with all options for wakeup
 * in that call
 */
void Chip_SYSCON_SetWakeup(uint32_t wakeupmask);

/**
 * @brief   Power down one or more blocks or peripherals
 * @param   powerdownmask   : OR'ed values of SYSCON_SLPWAKE_* values
 * @return  Nothing
 */
void Chip_SYSCON_PowerDown(uint32_t powerdownmask);

/*!
 * @brief   为模拟外设模块上电
 * @param   powerupmask : OR'ed values of SYSCON_PeriphPowerSel_T values
 * @return  Nothing
 */
void Chip_SYSCON_PowerUp(uint32_t powerupmask);

/*!
 * @brief 定义CLKOUT信号输出时钟源选项
 */
typedef enum
{
    eSYSCON_ClkoutSource_IRC12MHz      = 0, /*!< IRC oscillator.    */
    eSYSCON_ClkoutSource_OSC           = 1, /*!< Crystal oscillator (SYSOSC). */
    eSYSCON_ClkoutSource_WatchdogClock = 2, /*!< Watchdog oscillator. */
    eSYSCON_ClkoutSource_MainClock     = 3, /*!< Main clock. */
} SYSCON_ClkoutSource_T;

/*!
 * @brief 配置CLKOUT信号输出
 *
 * CLKOUT信号需要绑定到引脚上才能真正被外部观测设备观察到，因此还需要SWM及IOCON(默认配置)配合使用.
 *
 * @param source  选择输出时钟源, 参见#SYSCON_ClkoutSource_T.
 * @param divider 设定输出时信号的分频值, 有效范围为1-255, 当为0时关闭输出.
 */
static inline void Chip_SYSCON_SetCLKOUTSource(SYSCON_ClkoutSource_T source, uint32_t divider)
{
    LPC_SYSCON->CLKOUTSEL = (uint32_t)source; /* 选定时钟信号源. */
    LPC_SYSCON->CLKOUTDIV = divider; /* 设定输出分频值. */
    /* 在硬件上更新CLKOUT时钟源的设定. */
    LPC_SYSCON->CLKOUTUEN = 0x0;
    LPC_SYSCON->CLKOUTUEN = 0x1;
}

/*!
 * @brief 配置UART公用时钟源U_PCLK的分频
 *
 * U_PCLK = Main Clock / MainDiv x (1 + (FragMult / FragDiv))
 * 其中:
 * - MainDiv对应SYSCON_UARTCLKDIV[DIV]的值
 * - FragMult对应SYSCON_UARTFRGMULT[MULT]+1
 * - FragDiv对应SYSCON_UARTFRGDIV[DIV]+1
 * 此处得到的U_PCLK需要再经过各UART模块自己的分频最终生成该模块通信的波特率.
 *
 * @param mainDiv SYSCON_UARTCLKDIV[DIV]的设定值, 有效设定值为1-255, 若为0则关闭U_PCLK.
 * @param frgMult SYSCON_UARTFRGMULT[MULT]的设定值, 有效设定值为0-255
 * @param frgDiv  SYSCON_UARTFRGDIV[DIV]的设定值, 只能设定为0xFF. UM10800, P45, Table 40.
 */
static inline void Chip_SYSCON_ConfigUARTCommonClockDivier(uint32_t mainDiv, uint32_t fragMult, uint32_t fragDiv)
{
    LPC_SYSCON->UARTCLKDIV = mainDiv;
    LPC_SYSCON->UARTFRGMULT = fragMult;
    LPC_SYSCON->UARTFRGDIV = fragDiv;
}

/*!
 * @brief 配置UART公共波特率
 *
 * 在LPC单片机中，多个UART使用同一个波特率分频.每个UART内部仍有进一步的波特率发生器.
 *
 * @param mainClkHz 当前的Main Clock的频率
 * @param baudrate  设定目标波特率
 */
static inline void Chip_SYSCON_SetUARTCommonBaudrate(uint32_t mainClkHz, uint32_t baudrate)
{
    uint32_t mainDiv = mainClkHz / (baudrate * 16U);

    Chip_SYSCON_ConfigUARTCommonClockDivier(
        mainDiv,
        (mainClkHz % (baudrate * 16U)) * 256U / mainDiv,
        0xFF
    );
}

/**
 * @brief 定义访问外设寄存器区域的接口时钟
 *
 * 这个时钟实际上是AHB总线访问内存区使用，并不是外设模块本身的工作时钟。
 */
typedef enum
{
    SYSCON_AHBPeriphClk_SYS = 0,   /*!< System clock */
    SYSCON_AHBPeriphClk_ROM,       /*!< ROM clock */
    SYSCON_AHBPeriphClk_RAM,       /*!< RAM clock */
    SYSCON_AHBPeriphClk_FLASHREG,  /*!< FLASH register interface clock */
    SYSCON_AHBPeriphClk_FLASH,     /*!< FLASH array access clock */
    SYSCON_AHBPeriphClk_I2C0,      /*!< I2C0 clock */
    SYSCON_AHBPeriphClk_GPIO,      /*!< GPIO clock */
    SYSCON_AHBPeriphClk_SWM,       /*!< Switch matrix clock */
    SYSCON_AHBPeriphClk_SCT,       /*!< State configurable timer clock */
    SYSCON_AHBPeriphClk_WKT,       /*!< Self wake-up timer clock */
    SYSCON_AHBPeriphClk_MRT,       /*!< Multi-rate timer clock */
    SYSCON_AHBPeriphClk_SPI0,      /*!< SPI0 clock */
    SYSCON_AHBPeriphClk_SPI1,      /*!< SPI01 clock */
    SYSCON_AHBPeriphClk_CRC,       /*!< CRC clock */
    SYSCON_AHBPeriphClk_UART0,     /*!< UART0 clock */
    SYSCON_AHBPeriphClk_UART1,     /*!< UART1 clock */
    SYSCON_AHBPeriphClk_UART2,     /*!< UART2 clock */
    SYSCON_AHBPeriphClk_WWDT,      /*!< Watchdog clock */
    SYSCON_AHBPeriphClk_IOCON,     /*!< IOCON clock */
    SYSCON_AHBPeriphClk_ACOMP,     /*!< Analog comparator clock */

    /* LPC82x Specific Clocks */
    SYSCON_AHBPeriphClk_I2C1 = 21, /*!< I2C1 Clock */
    SYSCON_AHBPeriphClk_I2C2,      /*!< I2C2 Clock */
    SYSCON_AHBPeriphClk_I2C3,      /*!< I2C3 Clock */
    SYSCON_AHBPeriphClk_ADC,       /*!< 12-Bit ADC Clock */
    SYSCON_AHBPeriphClk_MTB = 26,  /*!< Macro Trace Buffer [USED FOR DEBUGGING] */
    SYSCON_AHBPeriphClk_DMA = 29,  /*!< DMA Clock */
} SYSCON_AHBPeriphClk_T;

/* Clock name alias */
#define SYSCON_AHBPeriphClk_I2C      SYSCON_AHBPeriphClk_I2C0
#define SYSCON_AHBPeriphClk_ACMP     SYSCON_AHBPeriphClk_ACOMP

static inline void Chip_SYSCON_EnablePeriphClock(SYSCON_AHBPeriphClk_T periph)
{
    LPC_SYSCON->SYSAHBCLKCTRL = (1 << periph) | (LPC_SYSCON->SYSAHBCLKCTRL & ~SYSCON_SYSAHBCLKCTRL_RESERVED);
}

/**
 * @brief   Disable system or peripheral clock
 * @param   clk : Clock to disable
 * @return  Nothing
 */
static inline void Chip_SYSCON_DisablePeriphClock(SYSCON_AHBPeriphClk_T clk)
{
    LPC_SYSCON->SYSAHBCLKCTRL &= ~((1 << clk) | SYSCON_SYSAHBCLKCTRL_RESERVED);
}

/**
 * @}
 */

#ifdef __cplusplus
}
#endif

#endif /* __SYSCON_8XX_H_ */
